#ifndef __INC_QGASV_H__
#define __INC_QGASV_H__

#ifdef _MSC_VER
#pragma once
#pragma warning(push)
#pragma warning(disable:4251)
#endif

#include "qgenv.h"
#include "qguims.h"
#include "qguvls.h"

//
class qgStub;
class qgDevice;
class qgImage;


//////////////////////////////////////////////////////////////////////////
// base

//
#define QGMM_CREATE						1
#define QGMM_FILE						2
#define QGMM_MEM						3
#define QGMM_ASYNC						0x8000

// 
enum qgRR
{
	QGRR_OK = 0,
	QGRR_WAIT = 1,
	QGRR_FAIL = -1,
};

//
enum qgRoam
{
	QGROAM_NONE,

	QGROAM_RES_1,
	QGROAM_RES_2,
	QGROAM_RES_3,
	QGROAM_RES_4,
	QGROAM_RES_5,
	QGROAM_RES_6,
	QGROAM_EVENT,
	QGROAM_MTL_1,
	QGROAM_MTL_2,
	QGROAM_MTL_3,
	QGROAM_OBJ_1,
	QGROAM_OBJ_2,
	QGROAM_OBJ_3,

	QGROAM_DSV
};

//
class KAPI qgObj : public kObjNode
{
	KCH_SUBCLASS(qgObj, kObjNode);
	KCH_FRIEND(qgDevice);

protected:
	kint				_rrsv : 16;
	qgRoam				_roam : 16;

	qgDevice*			_dev;

protected:
	qgObj();
	~qgObj();

public:
	qgDevice* GetDevice() { return _dev; }

protected:
	void _BaseRoam(qgRoam roam);
};

// update interface
struct qgIUpdate
{
	virtual bool Update(float adv) = 0;
};


//////////////////////////////////////////////////////////////////////////
// include
#include "qgdraw.h"
#include "qgmtrl.h"
#include "qgarpr.h"


//////////////////////////////////////////////////////////////////////////
// stubs

// flags (device & stub)
#define QGSDF_FULL						0x0001
#define QGSDF_LAYOUT					0x0002
#define QGSDF_NO_TEXT					0x0004		// NO TEXT. not NOT EXT
#define QGSDF_LATENCY					0x0008
#define QGSDF_NPERF						0x0040
#define QGSDF_AUTO						0x0080		// DEBUG ONLY
#define QGSDF_MSAA						0x0200
#define QGSDF_DITHER					0x0400		// DX9, OPENGL ONLY
#define QGSDF_BGRA						0x1000
#define QGSDF_INDP						0x4000
#define QGSDF_INFO						0x8000

// insi (device & stub)
#define QGSDI_ACTIVE					0x0001
#define QGSDI_VIRTUAL					0x0002
#define QGSDI_LAYOUT					0x0004
#define QGSDI_DROP						0x0008
#define QGSDI_CURSOR					0x0010
#define QGSDI_SCRSAVE					0x0020
#define QGSDI_GOPU						0x0100
#define QGSDI_EXF_1						0x0400
#define QGSDI_EXF_2						0x0800
#define QGSDI_ACS						0x1000
#define QGSDI_PAUSE						0x4000
#define QGSDI_EXIT						0x8000

// service event
enum qgSev
{
	QGSEV_SYSTEM,
	QGSEV_LAYOUT,
	QGSEV_ACTIVE,
	QGSEV_CURSOR,
	QGSEV_DROPFILE,
	QGSEV_MOUSEMOVE,
	QGSEV_MOUSEUP,
	QGSEV_MOUSEDOWN,
	QGSEV_MOUSEDOUBLE,
	QGSEV_MOUSEWHEEL,
	QGSEV_KEYDOWN,
	QGSEV_KEYUP,
	QGSEV_CHAR,
	QGSEV_MAX_VALUE		// 12
};

// loader class
enum qgSlcs
{
	QGSLCS_1,
	QGSLCS_2,
	QGSLCS_3,
	QGSLCS_MAX_VALUE,
	QGSLCS_FORCE = 0xFFFF,
};

//
struct qgSevSystem
{
	kpointer			handle;
	kuintptr			wparam;
	kuintptr			lparam;
	kint				mesg;
};

//
struct qgSevLayout
{
	kint				width;
	kint				height;
	bool				maximize;
};

// please do not use because it it not for all runtime
struct qgSevCursor
{
	kint				area;
};

//
struct qgSevMouseMove
{
	kint				x, y;
	kint				rx, ry;
	kushort				state;
};

//
struct qgSevMouseButton
{
	kint				x, y;
	kbyte				button;
	kbyte				extend;
	kushort				state;
};

//
struct qgSevMouseWheel
{
	kint				x, y;
	kshort				wheel;
	kushort				state;
};

//
struct qgSevKey
{
	kbyte				key;
	kbyte				extend;
	kushort				state;
};

//
struct qgSevChar
{
	kint				ch;
	kushort				state;
};

//
struct qgSevActive
{
	bool				active;
	float				advance;
};

//
struct qgSevDrop
{
	int					count;
	tpl::BlueStr<260>*	names;
};

//
typedef tpl::Function<bool(qgSevSystem&)> qgSevSystemFunc;
typedef tpl::Function<bool(qgSevLayout&)> qgSevLayoutFunc;
typedef tpl::Function<bool(qgSevActive&)> qgSevActiveFunc;
typedef tpl::Function<bool(qgSevCursor&)> qgSevCursorFunc;
typedef tpl::Function<bool(qgSevDrop&)> qgSevDropFunc;
typedef tpl::Function<bool(qgSevMouseMove&)> qgSevMouseMoveFunc;
typedef tpl::Function<bool(qgSevMouseButton&)> qgSevMouseButtonFunc;
typedef tpl::Function<bool(qgSevMouseWheel&)> qgSevMouseWheelFunc;
typedef tpl::Function<bool(qgSevKey&)> qgSevKeyFunc;
typedef tpl::Function<bool(qgSevChar&)> qgSevCharFunc;
typedef tpl::Function<bool(kpointer)> qgSevFunc;

// stub, abstract
class KAPI qgStub : public kObj
{
	KCH_SUBCLASS(qgStub, kObj);

public:
	typedef tpl::HashTable<kuint, qgSevFunc> SevHash;
	typedef tpl::VecArray<qgReader*> SrdArray;
	typedef tpl::VecArray<qgWriter*> SwtArray;

protected:
	kuint			_flags;
	kuint			_insts;

	kint			_delay;
	kuintptr		_polls;

	double			_runtime;
	double			_runfps;
	float			_advance[4];

	tpl::Rect		_bound;
	tpl::Rect		_sysbnd;
	tpl::Point		_limsize;
	tpl::Point		_size;

	qgUimMouse		_mouse;
	qgUimKey		_key;

	tpl::String		_btext;
	kpointer		_handle;

	kTimer*			_timer;

	SevHash*		_els[QGSEV_MAX_VALUE];
	kuint			_elx;

	SrdArray		_svrd[QGSLCS_MAX_VALUE];
	SwtArray		_svwt;

protected:
	qgStub();
	~qgStub();

public:
	virtual bool Close();
	virtual bool Layout();
	virtual bool Poll();
	virtual bool Run();

	virtual bool GetControllerInput(kint slot, qgUimControllerInput* i);
	virtual bool GetControllerInfo(kint slot, qgUimControllerInfo* n);

	virtual void SetKeyDown(kbyte key, bool isdown);
	virtual bool SetControllerEnable(bool v);
	virtual bool SetControllerVibration(kint slot, const qgUimControllerVibration& v);

	virtual void SetDropEnable(bool v);
	virtual void SetAcsEnable(bool v);
	virtual void SetImeEnable(bool v);

	virtual bool SetText(const char* buffer);
	virtual void SetLimitSize(kint width, kint height);

	virtual void HoldMouse(bool v);
	virtual kintptr SystemProc(const qgSevSystem& e);

	bool GetMouse(qgUimMouse* v);
	bool GetKeyTable(qgUimKey* ptr);
	bool IsKeyDown(kbyte key);
	bool GetText(char* buffer, kint size);

	kuint AddSystemHandler(const qgSevSystemFunc& func);
	kuint AddLayoutHandler(const qgSevLayoutFunc& func);
	kuint AddActiveHandler(const qgSevActiveFunc& func);
	kuint AddCursorHandler(const qgSevCursorFunc& func);
	kuint AddDropHandler(const qgSevDropFunc& func);
	kuint AddMouseMoveHandler(const qgSevMouseMoveFunc& func);
	kuint AddMouseUpHandler(const qgSevMouseButtonFunc& func);
	kuint AddMouseDownHandler(const qgSevMouseButtonFunc& func);
	kuint AddMouseDoubleHandler(const qgSevMouseButtonFunc& func);
	kuint AddMouseWheelHandler(const qgSevMouseWheelFunc& func);
	kuint AddKeyDownHandler(const qgSevKeyFunc& func);
	kuint AddKeyUpHandler(const qgSevKeyFunc& func);
	kuint AddCharHandler(const qgSevCharFunc& func);
	bool RemoveHandler(kuint evid);
	bool CanInvokeHandler(qgSev sev);
	bool InvokeHandler(qgSev sev, kpointer arg);
	bool UnsafeInvokeHandler(qgSev sev, kpointer arg);

	void RegisterReader(qgSlcs slt, qgReader* ptr);
	bool PerformRead(kObj* ctnr, kconstpointer data, kint size, kint flags, qgSlcs slt = QGSLCS_FORCE);

	void RegisterWriter(qgWriter* ptr);
	bool PerformWrite(kStream* st, kObj* ctnr, kint flags);

	kpointer GetHandle() { return _handle; }
	kint GetFlags(kint mask = 0) { return K_OMASK(_flags, mask); }
	kint GetInsts(kint mask = 0) { return K_OMASK(_insts, mask); }

	const tpl::Rect& GetBound() { return _bound; }
	const tpl::Rect& GetSysBound() { return _sysbnd; }
	const tpl::Point& GetLimitSize() { return _limsize; }
	const tpl::Point& GetSize() { return _size; }

	kTimer* GetTimer() { return _timer; }
	double GetRunTime() { return _runtime; }
	double GetRunFps() { return _runfps; }
	float GetAdvance() { return _advance[0]; }
	float GetAdvance(kint nth) { return (ksize_t)nth < 4 ? _advance[nth] : 0.0f; }

	void SetDelay(kint value) { _delay = K_CLAMP(value, 1, 1000); }
	kint GetDelay() { return _delay; }

	template <ksize_t SIZE>
	bool GetText(tpl::BlueStr<SIZE>& bs)
	{
		return GetText(bs.Data(), bs.Size());
	}

protected:
	bool _ProcessMultiClick(kint button, kint method);

public:
	static const char* GetInternalClassName();
	static void SetInternalClassName(const char* name);

public:
	static qgStub* New(const kRt& bound, const char* text, kint flags);
	static qgStub* New(kint width, kint height, const char* text, kint flags);
	static qgStub* NewVirtual(kpointer handle);
};


//////////////////////////////////////////////////////////////////////////
// device

// device update info
struct qgDevNfo
{
	kuintptr			frames;
	kuintptr			invokes;
	kuintptr			entries;
	bool				flush;
};

// device specfics
struct qgDevSpcf
{
	bool				draw_2d_auto_set;
};

// device capabilities
struct qgDevCaps
{
	char				renderer[64];
	char				vendor[64];

	bool				supp_shader;
	bool				supp_stencil;
	bool				supp_multi_texture;
	bool				supp_compressed_texture;
	bool				supp_texture_npo2;
	bool				supp_occlusion;
	bool				supp_byte_glyph_texture;

	kbyte				max_texture_count;
	kbyte				max_offtex_count;
	kbyte				max_clip_planes;
	kbyte				val_depth_bpp;

	kuint				max_texture_dimension;
	kuint				max_indices_count;
	kuint				max_gs_vertices_count;
	kint				flag_texture_image;

	float				max_texture_LOD_bias;

	float				dim_alias_line[2];
	float				dim_alias_point[2];
	float				dim_smt_line[2];
	float				dim_smt_point[2];

	kushort				version;
	kushort				version_shader;
};

// device transform data
struct qgDevTrfm
{
	tpl::Mat4			tm[QGTMS_MAX_VALUE];
	tpl::Mat4			vipr;
	tpl::Mat4			invv;

	tpl::Mat4			otm;
	tpl::Mat4			tfm;

	tpl::Point			sd;		// screen dimension

	qgViewPort			vp;		// view port
};

// device present differed data
struct qgDevPrsn : public tpl::MemZero
{
	// color
	tpl::Color			cr_bg;

	// lighting
	tpl::Color			l_ambi;
	tpl::Color			l_fill;
	kint				l_sop;

	// material
	qgCmpOp				m_aop;
	float				m_aref;
	kany				m_factor[QGSGTEX_MAX_VALUE];
	qgTexture*			m_tex[QGSGTEX_MAX_VALUE];
	qgSplr*				m_splr[QGSGTEX_MAX_VALUE];
	qgRasz*				m_rasz;
	qgBlnd*				m_blnd;
	qgShader*			m_shd;

	// off screen
	kint				mo_cnt;
	qgTexture*			mo_tex[QGSGOFF_MAX_VALUE];

	// draw
	qgTopology			w_tpg;
	kint				w_ioff;
	qgLayout*			w_lo;
	qgBuffer*			w_ib;
	qgBuffer*			w_vb[QGSGVERT_MAX_VALUE];

	// wedge
	kint				wb_cnt;
	const tpl::Mat4*	wb_pcb;

	// bind flags
	bool				bd_tex[QGSGTEX_MAX_VALUE];
	bool				bd_splr[QGSGTEX_MAX_VALUE];
	bool				bd_rasz;
	bool				bd_blnd;
	bool				bd_shd;

	bool				bd_tpg;
	bool				bd_lo;
	bool				bd_ib;
	bool				bd_vb[QGSGVERT_MAX_VALUE];
};

// device default environment bases
struct qgDevDebz : public tpl::MemZero
{
	qgBlnd*				blnd;
	qgBlnd*				bglyph;
	qgRasz*				rasz;
	qgSplr*				splr;
	qgSplr*				slinear;

	tpl::Vec4			propv[4];
	tpl::Mat4			propm[4];
};

// device container
class qgDevContainer : public tpl::MemBase
{
	KCH_SELF(qgDevContainer);
	KCH_NOEC(qgDevContainer);
	KCH_FRIEND(qgDevice);

protected:
	kuintptr			cnt, acc, ldn;
	qgRoam				roam;

	kObjMukum			rasz, blnd;
	kObjMukum			splr, tex;
	kObjMukum			shd, los;
	kObjList			dbs;
	kObjList			evs, obj, sub, node;

private:
	qgDevContainer();
	~qgDevContainer();

public:
	void Dispose(bool waitforloader);

	bool Add(qgRoam roam, qgObj* node);
	bool Remove(qgRoam roam, qgObj* node);
	qgObj* Find(qgRoam roam, kconstpointer key);
	void Signal(const char* signal, kpointer argdata);
};

// device
class KAPI qgDevice : public kObj
{
	KCH_SUBCLASS(qgDevice, kObj);
	KCH_FRIEND(qgObj);

protected:
	qgStub*				_stub;
	kMount*				_mount;

	kSpinLock			_dlock;
	kSpinLock			_mlock;

	qgDevNfo			_nfo;
	qgDevSpcf			_spcf;
	qgDevCaps			_caps;
	qgDevTrfm			_trfm;

	qgDevPrsn*			_sprn;
	qgDevDebz*			_sdeb;
	qgDevContainer*		_ctnr;

	kint				_sevlo;

	tpl::Vec2			_vsize;

public:
	static qgDevice*	CurrentDevice;

protected:
	qgDevice();
	~qgDevice();

public:
	bool Run();

	//
	qgStub* GetStub() { return _stub; }
	kMount* GetMount() { return _mount; }

	bool GetSpecific(qgRenderSpecific nth, kint* ret);
	const qgDevNfo& GetNfo() { return _nfo; }
	const qgDevTrfm& GetTrfm() { return _trfm; }
	const qgDevCaps& GetCaps() { return _caps; }

	qgDevPrsn* GetPrsn() { return _sprn; }
	qgDevContainer* GetContainer() { return _ctnr; }

	//
	bool CanFlush() { return _nfo.flush; }

	//
	bool SetSpecific(qgRenderSpecific nth, kint value);
	void SetPropVec(kint n, const tpl::Vec4& v);
	void SetPropMat(kint n, const tpl::Mat4& m);
	void SetMatPalette(kint cnt, const tpl::Mat4* pcb);

	//
	kint Mount(const char* path = NULL, bool autoarchive = true);

	kStream* FileOpen(const char* filename, bool indirect) { return indirect ? kMount::FileOpenIndirect(_mount, filename) : kMount::FileOpen(_mount, filename); }
	kpointer FileRead(const char* filename, kint* size) { return kMount::FileRead(_mount, filename, size); }
	kchar* FilePath(const char* path) { return kMount::BuildPath(_mount, path); }

public:
	const char* GetDeviceName();

	// draw signal
	virtual bool Layout(const qgSevLayout& e);

	virtual bool Enter(kint clearance = 0xFF);
	virtual bool Leave();
	virtual bool Flush();

	// draw element
	virtual void SetBackground(const tpl::ColorValue& color);
	virtual bool SetTM(qgTmType tm, const tpl::Mat4& m);

	void SetTexture(kint stage, qgTexture* tex);
	void SetSplr(kint stage, qgSplr* splr);
	void SetRasz(qgRasz* rasz);
	void SetBlnd(qgBlnd* blnd);
	void SetShader(qgShader* shader);

	void SetTopology(qgTopology tpg);
	bool SetLayout(qgLayout* lo);
	bool SetBuffer(qgBuffer* buffer);
	bool SetVertexBuffer(kint stage, qgBuffer* buffer);
	bool SetIndexBuffer(kint offset, qgBuffer* buffer);

	// draw primitive
	virtual void Draw(kint vertexcount, kint vertexstart);
	virtual void DrawIndexed(kint indexcount, kint vertexstart);

	virtual void DrawEnterIndirect(kint vertexcount, kint vertexstride, kpointer* outdata);
	virtual void DrawLeaveIndirect();

	virtual void DrawRect(const tpl::Rect& bound, const tpl::ColorValue& color, qgTexture* tex = NULL, const tpl::Quad* coord = NULL);
	virtual void DrawGlyph(const tpl::Rect& bound, const tpl::ColorValue& color, qgTexture* tex);
	virtual void DrawPolyLine(kint count, const tpl::Point* pts, const tpl::ColorValue* colors = NULL);
	virtual void DrawPolyStrip(kint count, const tpl::Point* pts, const tpl::ColorValue* colors = NULL, qgTexture* tex = NULL, const tpl::Vec2* coords = NULL);

	// utility
	virtual qgImage* ScreenCapture() { return NULL; }

	// create 
	qgBuffer* CreateBuffer(qgBufferType type, kint stride, kint count, kint flags, kconstpointer data);
	qgBuffer* CreateIndexBuffer(kint count, kint flags, bool is32bit);
	qgBuffer* CreateIndexBuffer(const kuint* data, kint count, kint flags);
	qgBuffer* CreateIndexBuffer(const kushort* data, kint count, kint flags);
	template <ksize_t COUNT> qgBuffer* CreateIndexBuffer(const kuint(&data)[COUNT], kint flags) { return CreateIndexBuffer(data, COUNT, flags); }
	template <ksize_t COUNT> qgBuffer* CreateIndexBuffer(const kushort(&data)[COUNT], kint flags) { return CreateIndexBuffer(data, COUNT, flags); }
	qgBuffer* CreateVertexBuffer(kint count, qgLayout* lo, kint stage, kint flags);
	qgBuffer* CreateVertexBuffer(kconstpointer data, kint count, qgLayout* lo, kint stage, kint flags);
	template <typename T, ksize_t COUNT> qgBuffer* CreateVertexBuffer(const T (&data)[COUNT], qgLayout* lo, kint stage, kint flags) { return CreateVertexBuffer((kconstpointer)data, COUNT, lo, stage, flags); }
	qgLayout* CreateLayout(kint count, const qgLayoutElement* elms);
	template <ksize_t COUNT> qgLayout* CreateLayout(const qgLayoutElement(&elms)[COUNT]) { return CreateLayout(COUNT, elms); }
	qgTexture* CreateTexture(const char* name, kint width, kint height, kint depth, kint mips, kint flags, qgColorFormat fmt = QGCF_NONE);
	qgTexture* CreateTexture(const char* name, qgImage* img, kint flags);
	qgTexture* CreateTexture(const char* name, kconstpointer data, kint size, kint flags);
	qgTexture* CreateTexture(const char* filename, kint flags);
	qgDepthBuffer* CreateDepthBuffer(kint width, kint height);
	qgShader* CreateShader(const char* name = NULL);
	qgRasz* CreateRasz(const qgRaszData& data);
	qgBlnd* CreateBlnd(const qgBlndData& data);
	qgSplr* CreateSplr(const qgSplrData& data);

	// internal use only
	virtual bool _SetRenderTarget(qgTexture* tex) = 0;
	virtual kint _AcquireRenderTarget(qgTexture* tex);
	virtual kint _ReleaseRenderTarget(qgTexture* tex);

public: //protected:
	virtual bool _Initialize(qgStub* stub, kint flags);
	virtual bool _ResetPrepare(const tpl::Point& size, bool prepare);
	virtual void _Finalize();

	virtual qgBuffer* _RoamImplBuffer(const qgArprBuffer& arpr) = 0;
	virtual qgLayout* _RoamImplLayout(qgLayoutData* data) = 0;
	virtual qgTexture* _RoamImplTexture(const qgArprTexture& arpr) = 0;
	virtual qgTexture* _RoamImplTexture(const qgArprImage& arpr) = 0;
	virtual qgTexture* _RoamImplMotionTexture(const char* filename, kint flags) = 0;
	virtual qgDepthBuffer* _RoamImplDepthBuffer(const tpl::Point& size) = 0;
	virtual qgShader* _RoamImplShader(const char* name) = 0;
	virtual qgRasz* _RoamImplRasz(const qgRaszData& data) = 0;
	virtual qgBlnd* _RoamImplBlnd(const qgBlndData& data) = 0;
	virtual qgSplr* _RoamImplSplr(const qgSplrData& data) = 0;

	bool _RoamLoad(qgRoam roam, qgObj* node);
	bool _RoamUnload(qgRoam roam, qgObj* node);
	void _RoamSignal(const char* signal, kpointer argdata = NULL);
	qgObj* _RoamFind(qgRoam roam, kconstpointer key);

public:
	static qgDevice* New(const char* dev, const tpl::Rect& bound, kint flags);
	static qgDevice* New(const char* dev, kint width, kint height, kint flags);
	static qgDevice* NewVirtual(const char* dev, kpointer handle, kint flags);
	static void ActiveDevice(qgDevice* dev);
};

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#endif	// __INC_QGASV_H__
